package main

import (
	"github.com/ngaut/log"
	"server/backup/model"
	"time"
	"fmt"
	"strconv"
	"global/structs"
	"math"
	"github.com/go-xorm/xorm"
)

type Backup struct{
	tablePrefix 	string

	// 备份表名
	tableName 		string

	tableNumber 	int

	// 表剩余容量
	capacity 		int64

	c 				*Core
}

func NewBackup() *Backup {
	return &Backup{
		tablePrefix: 	"trust_coin_done_",
	}
}

// 初始化
func (m *Backup) initBaseInfo() error {
	var err error
	m.tableName, err = m.getTableName()
	if err != nil {
		return err
	}

	// 检查表是否存在
	if err := m.checkOrCreateTable(); err != nil {
		return err
	}

	// 检查备份表剩余空间
	m.capacity, err = m.getCapacityOfBackupTable()
	if err != nil {
		return err
	}

	m.clearTableIndexInCache()

	return nil
}

func (m *Backup) resetTable() error {

	for {
		m.tableNumber++
		m.tableName = fmt.Sprintf("%s%d", m.tablePrefix, m.tableNumber)

		exists, err := model.TrustCoinModel.IsTableExists(m.tableName)
		if err != nil {
			return err
		}

		if !exists {
			break
		}
	}

	// 更新 system config
	err := model.SystemCoinfigModel.UpdateValue(m.tableNumber)
	if err != nil {
		return err
	}

	if _, err := model.TrustCoinModel.CreateTable(m.tableName); err != nil {
		return err
	}

	m.clearTableIndexInCache()

	m.capacity = DATABASE_ITEM_LIMIT

	return nil
}

func (m *Backup) run(c *Core) {
	m.c = c
	if err := m.initBaseInfo(); err != nil {
		log.Errorf("init error: %s", err.Error())
		return
	}

	go func() {
		for {

			log.Infof("left capacity: %d", m.capacity)
			log.Infof("backup table name: %s", m.tableName)

			// 创建新表
			if m.capacity <= 0 {
				err := m.resetTable()
				if err != nil {
					log.Errorf("reset table error: %s", err.Error())
					return
				}
				log.Infof("create new table success, name: %s", m.tableName)
			}

			items, err := m.getTrustCoinItems()
			if err != nil {
				log.Errorf("get trust coin list error: %s", err.Error())
				return
			}

			count := len(items)
			log.Infof("get trust coin items: %v", len(items))

			if count == 0 {
				time.Sleep(10 * time.Second)
				continue
			}

			// save to backup db
			err = m.saveToBackupDB(items, count)
			if err != nil {
				log.Errorf("save trust to backup db error: %s", err.Error())
				return
			}

			m.capacity = m.capacity - int64(count)

			time.Sleep(2 * time.Second)
		}
	}()
}

//
func (m *Backup) clearTableIndexInCache() {
	conn := m.c.GetBaseRedisPool().Get()
	defer conn.Close()
	_, err := conn.Do("HDEL", "config:key", "backup_db_num")
	if err != nil {
		log.Errorf("delete backup db index number error")
	}
}

// 事务操作添加到新表并删除原记录
func (m *Backup) saveToBackupDB(items []structs.TrustCoin, counts int) error {
	sess := model.MainDB.NewSession()
	defer sess.Close()

	err := sess.Begin()
	if err != nil {
		return err
	}

	times := math.Ceil(float64(counts) / 10)
	for i:=0; i<int(times); i++ {
		newItems := make([]structs.TrustCoin, 0)
		ids := make([]int, 0)
		for j := 0; j < 10; j++ {
			key := i * 10 + j
			if counts == key {
				break
			}
			newItems = append(newItems, items[key])
			ids = append(ids, items[key].Id)
		}

		// 添加到新表
		err := m.insertIntoNewTable(sess, newItems)
		if err != nil {
			sess.Rollback()
			log.Errorf("insert into new table error: %s", err.Error())
			return err
		}
		
		// 从旧表删除
		err = m.removeFromOriginalTable(sess, ids)
		if err != nil {
			sess.Rollback()
			return err
		}

	}

	if err = sess.Commit(); err != nil {
		return err
	}

	return nil
}

// 删除
func (m *Backup) removeFromOriginalTable(sess *xorm.Session, ids []int) error {
	rows, err := model.TrustCoinModel.RemoveFromOriginalTable(sess, ids)
	if err != nil {
		return err
	}
	if int(rows) != len(ids) {
		return fmt.Errorf("remove items not eq ids, ids: %v", ids)
	}
	return nil
}

// 插入
func (m *Backup) insertIntoNewTable(sess *xorm.Session, items []structs.TrustCoin) error {
	rows, err := model.TrustCoinModel.InsertIntoBackupTable(sess, m.tableName, items)
	if err != nil {
		return err
	}
	if int(rows) != len(items) {
		return fmt.Errorf("insert into new table affected rows is not eq insert items")
	}
	return nil
}

// 获取委托单
func (m *Backup) getTrustCoinItems() ([]structs.TrustCoin, error) {
	return model.TrustCoinModel.GetItems(m.getItems())
}

// 需要获取的数量
func (m *Backup) getItems() int64 {
	if m.capacity < QUERY_ITEM_LIMIT {
		return m.capacity
	}
	return QUERY_ITEM_LIMIT
}

// 获取备份表剩余容量
func (m *Backup) getCapacityOfBackupTable() (int64, error) {
	total, err := model.TrustCoinModel.GetCount(m.tableName)
	if err != nil {
		return 0, err
	}

	left := DATABASE_ITEM_LIMIT - total
	if left < 0 {
		return 0, nil
	}
	return left, nil
}

// 检查表是否存在，不存在则创建
func (m *Backup) checkOrCreateTable() error {
	exists, err := model.TrustCoinModel.IsTableExists(m.tableName)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}
	if _, err = model.TrustCoinModel.CreateTable(m.tableName); err != nil {
		return err
	}
	return nil
}

// 获取表名
func (m *Backup) getTableName() (string, error) {
	if m.tableName != "" {
		return m.tableName, nil
	}

	// 从数据库获取
	num, err := model.SystemCoinfigModel.GetBackupDBConfig()
	if err != nil {
		return "", err
	}

	if num == "" {
		return "", fmt.Errorf("no backup db name returned")
	}

	m.tableNumber, err = strconv.Atoi(num)
	if err != nil {
		return "", err
	}

	return m.tablePrefix + num, nil
}
